1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.math.vector;
12 import core.math : sqrt, sin, cos;
13 float abc(int a, int b) { return cast(float)a+b;}
14 
15 struct Vector(uint N, T)
16 {
17     @"format" string toString()
18     {
19         static if(N == 2) return "<$x, $y>";
20         else static if(N == 3) return "<$x, $y, $z>";
21         else static if(N == 4) return "<$x, $y, $z, $w>";
22     }
23     static assert(N >= 2 && N <= 4, "Vector is only implemented for 2, 3 and 4 dimensions");
24     private alias VectorN = Vector!(N, T);
25     @nogc @safe nothrow
26     {
27         static if(N == 2)
28         {
29             this(T x, T y)
30             {
31                 static if(isSIMD)
32                 {
33                     this.x = x;
34                     this.y = y;
35                 }
36                 else
37                     data = [x, y];
38             }
39             this(T[2] v)
40             {
41                 static if(isSIMD)
42                 {
43                     x = v[0];
44                     y = v[1];
45                 }
46                 else
47                     data = [v[0], v[1]];
48             }
49         }
50         else static if (N == 3)
51         {
52             this(T x, T y, T z)
53             {
54                 static if(isSIMD)
55                 {
56                     this.x = x;
57                     this.y = y;
58                     this.z = z;
59                 }
60                 else
61                     data = [x, y, z];
62             }
63             this(T[3] v)
64             {
65                 static if(isSIMD)
66                 {
67                     x = v[0];
68                     y = v[1];
69                     z = v[2];
70                 }
71                 else
72                     data = [v[0], v[1], v[2]];
73             }
74         }
75         else static if (N == 4)
76         {
77             this(T x, T y, T z, T w = 1)
78             {
79                 static if(isSIMD)
80                 {
81                     this.x = x;
82                     this.y = y;
83                     this.z = z;
84                     this.w = w;
85                 }
86                 else
87                     data = [x, y, z, w];
88             }
89             this(T[4] v)
90             {
91                 static if(isSIMD)
92                 {
93                     x = v[0];
94                     y = v[1];
95                     z = v[2];
96                     w = v[3];
97                 }
98                 else
99                     data = [v[0], v[1], v[2], v[3]];
100             }
101         }
102         static if(N >= 3)
103         {
104             pragma(inline, true)
105             {
106                 Vector2 xy(){return Vector2(x, y);}
107                 Vector2 xy(Vector2 v){x = v.x; y = v.y; return v;}
108                 Vector2 yx(){return Vector2(y, x);}
109                 Vector2 yx(Vector2 v){y = v.x; x = v.y; return yx;}
110             }
111         }
112         static if(N == 4)
113         {
114             pragma(inline, true)
115             {
116                 Vector3 xyz(){return Vector3(x, y, z);}
117                 Vector3 xyz(Vector3 v){x = v.x; y = v.y;z = v.z; return v;}
118             }
119         }
120         pragma(inline, true) T opIndexUnary(string op)(size_t index) if(op == "-")
121         {
122             assert(index >= 0 && index <= N);
123             return mixin(op ~ "data[",index,"];");
124         }
125         pragma(inline, true) ref T[N] opCast() const
126         {
127             return data;
128         }
129         auto opUnary(string op)() inout if(op == "-")
130         {
131             static if(N == 2) return Vector!(2, T)(-data[0], -data[1]);
132             static if(N == 3) return Vector!(3, T)(-data[0], -data[1], -data[2]);
133             static if(N == 4) return Vector!(4, T)(-data[0], -data[1], -data[2], -data[3]);
134         }
135         float dot()(auto ref VectorN other) inout
136         {
137             float ret = 0;
138             for(int i = 0; i < N; i++)
139                 ret+= data[i]*other[i];
140             return ret;
141         }
142         inout float mag()
143         {
144             float ret = 0;
145             for(int i = 0; i < N; i++)
146                 ret+= data[i]*data[i];
147             return sqrt(ret);
148         }
149         inout float magSquare()
150         {
151             float ret = 0;
152             for(int i = 0; i < N; i++)
153                 ret+= data[i]*data[i];
154             return ret;
155         }
156         void normalize()
157         {
158             const float m = mag();
159             if(m != 0)
160                 data[]/=m;
161         }
162 
163         float distance(VectorN other)
164         {
165             float dx = (other.x-x);
166             dx*=dx;
167             float dy = other.y-y;
168             dy*=dy;
169             
170             static if(N >= 3)
171             {
172                 float dz = other.z-z;
173                 dz*=dz;
174                 static if(N == 4)
175                 {
176                     float dw = other.w - w;
177                     dw*=dw;
178                     return sqrt(dx+dy+dz+dw);
179                 }
180                 else
181                     return sqrt(dx+dy+dz);
182             }
183             static if(N == 2)
184                 return sqrt(dx+dy);
185         }
186 
187         VectorN unit() inout
188         {
189             const float m = mag();
190             if(m != 0)
191                 return this / m;
192             return this;
193         }
194         
195         VectorN project()(auto ref VectorN reference) inout
196         {
197             auto n = reference.unit;
198             return n * dot(reference);
199         }
200 
201         static if(N == 3)
202         {
203             VectorN axisAngle(in VectorN axis, float angle) inout
204             {
205                 auto n = axis.unit;
206                 auto proj = n* axis.dot(n);
207                 auto perpendicular = this - proj;
208                 auto rot = perpendicular*cos(angle) + n.cross(perpendicular)*sin(angle);
209                 return proj + rot;
210             }
211 
212 
213             VectorN cross()(auto ref VectorN other) inout
214             {
215                 return VectorN(data[1]*other[2] - data[2]-other[1],
216                             -(data[0]*other[2]- data[2]*other[0]),
217                             data[0]*other[1] - data[1]*other[0]);
218             }
219         }
220 
221         static float Dot()(auto ref Vector!N first, auto ref Vector!N second){return first.dot(second);}
222 
223         static if(N >= 3)
224         {
225             VectorN rotateZ(float radians)
226             {
227                 const float c = cos(radians);
228                 const float s = sin(radians);
229 
230                 static if(N == 3)
231                     return VectorN(x*c - y*s, y*c + s*x, z);
232                 else
233                     return Vector!(N, T)(x*c - y*s, y*c + s*x, z, w);
234             }
235         }
236 
237         pragma(inline, true)
238         {
239             VectorN opBinary(string op)(in VectorN rhs) inout if(op == "*" || op == "/" || op == "+" || op == "-")
240             {
241                 VectorN ret;
242                 for(size_t i = 0; i < N; i++)   
243                 {
244                     ret[i] = mixin("data[i] ", op,"rhs[i]");
245                     version(HipMathSkipNanCheck){}
246                     else static if(op == "/" || op == "-") assert(ret[i] == ret[i]); //Check for float.nan
247                 }
248                 return ret;
249             }
250             VectorN opBinary(string op)(float rhs) inout
251             {
252                 VectorN ret;
253                 for(size_t i = 0; i < N; i++)
254                 {
255                     ret[i] = mixin("data[i]", op, "rhs");
256                     version(HipMathSkipNanCheck){}
257                     else static if(op == "/" || op == "-") assert(ret[i] == ret[i]); //Check for float.nan
258                 }
259                 return ret;
260             }
261 
262             alias opBinaryRight = opBinary;
263             auto opOpAssign(string op)(VectorN other) return
264             {
265                 version(HipMathSkipNanCheck)
266                     mixin("data[]",op,"= other.data[];");
267                 else
268                 {
269                     for(size_t i = 0; i < N; i++)
270                     {
271                         mixin("data[i]",op,"= other[i];");
272                         static if(op == "/" || op == "-") 
273                         assert(data[i] == data[i]); //Check for float.nan
274                     }
275                 }
276                 return this;
277             }
278 
279             auto opOpAssign(string op)(float value) return
280             {
281                 version(HipMathSkipNanCheck)
282                     mixin("data[]",op,"= value;");
283                 else
284                 {
285                     for(size_t i = 0; i < N; i++)
286                     {
287                         mixin("data[i]",op,"= value;");
288                         static if(op == "/" || op == "-") 
289                         assert(data[i] == data[i]); //Check for float.nan
290                     }
291                 }
292                 return this;
293             }
294 
295             ref VectorN opAssign(in VectorN other) return
296             {
297                 for(size_t i = 0; i < N; i++)
298                     data[i] = other[i];
299                 return this;
300             }
301 
302             ref VectorN opAssign(in T[N] other) return
303             {
304                 for(size_t i = 0; i < N; i++)
305                     data[i] = other[i];
306                 return this;
307             }
308 
309             static VectorN zero()
310             {
311                 return VectorN.init;
312             }
313 
314         }
315 
316         private enum isSIMD = false;
317         static if(isSIMD)
318         {
319             alias TSimd = mixin(T.stringof~"4");
320             private TSimd data; //int2, float2 are not supported for some reason
321         }
322         else
323         {
324             ///Use 0 init for every type of number, float starts with nan which always involve into setting it to
325             //a reasonable value
326             private T[N] data = 0;
327         }
328 
329         pragma(inline, true)
330         {
331             @trusted inout auto ref x() return 
332             {
333                 static if(isSIMD)
334                     return (cast(T*)&data)[0];
335                 else
336                     return data[0];
337             }
338             @trusted inout auto ref y() return 
339             {
340                 static if(isSIMD)
341                     return (cast(T*)&data)[1];
342                 else
343                     return data[1];
344             }
345             static if(N >= 3)
346             {
347                 @trusted inout auto ref z() return 
348                 {
349                     static if(isSIMD)
350                         return (cast(T*)&data)[2];
351                     else
352                         return data[2];
353                 }
354             }
355             static if(N == 4)
356             {
357                 @trusted inout auto ref w() return 
358                 {
359                     static if(isSIMD)
360                         return (cast(T*)&data)[3];
361                     else
362                         return data[3];
363                 }
364             }
365 
366             inout auto ref opIndex(size_t index)
367             {
368                 return data[index];
369             }
370         }
371 
372     }
373 }
374 
375 alias Vector2 = Vector!(2, float);
376 alias Vector3 = Vector!(3, float);
377 alias Vector4 = Vector!(4, float);